using System;
using MbUnit.Framework;
using NEnsure.Containers;
using Rhino.Mocks;

namespace NEnsure.UnitTests
{
	[TestFixture]
	public class RuleContainerTests
	{
		private MockRepository _mocks;
		private IBusinessRule<string> _rule;
		private IBusinessRule<string> _rule2;
		private IBusinessRule<int> _rule3;
		private IRuleContainer<string> _container;
		private int _firedRunningRuleEventCount;
		private int _firedRanRuleEventCount;

		[SetUp]
		public void Init()
		{
			_firedRanRuleEventCount = 0;
			_mocks = new MockRepository();
			_firedRunningRuleEventCount = 0;
			_rule = _mocks.StrictMock<IBusinessRule<string>>();
			_rule2 = _mocks.StrictMock<IBusinessRule<string>>();
			_rule3 = _mocks.StrictMock<IBusinessRule<int>>();
			_container = new RuleContainer<string>("PropertyName", "PropertyValue");

			SetupResult.For(_rule.IsBrokenBy("PropertyValue")).Return(true);
			SetupResult.For(_rule2.IsBrokenBy("PropertyValue")).Return(false);
			SetupResult.For(_rule.ErrorFormat).Return("Error Format");
			SetupResult.For(_rule2.ErrorFormat).Return("Error Format");

			_mocks.ReplayAll();
		}

		[Test]
		public void Should_Set_Property_Name_Correctly()
		{
			Assert.AreEqual("PropertyName", _container.PropertyName);
		}

		[Test]
		[ExpectedArgumentNullException]
		public void Should_Die_If_Property_Name_Is_Nothing()
		{
			IRuleContainer<string> container = new RuleContainer<string>(string.Empty, null);
		}

		[Test]
		public void Should_Set_Property_Value_Correctly()
		{
			Assert.AreEqual("PropertyValue", _container.PropertyValue);
		}

		[Test]
		public void Satisfy_Should_Be_Able_To_Add_Multiple_Business_Rules_At_A_Time()
		{
			_container.Satisfies(_rule, _rule2);

			Assert.AreEqual(2, _container.Rules.Length);
			Assert.AreEqual(_rule, _container.Rules[0]);
			Assert.AreEqual(_rule2, _container.Rules[1]);
		}

		[Test]
		public void Satisfy_Should_Not_Add_Null_Rules()
		{
			_container.Satisfies(_rule, null);

			Assert.AreEqual(1, _container.Rules.Length);
			Assert.AreEqual(_rule, _container.Rules[0]);
		}

		[Test]
		public void Satisfy_Should_Not_Add_Duplicate_Rules()
		{
			_container.Satisfies(_rule, _rule);

			Assert.AreEqual(1, _container.Rules.Length);
		}

		[Test]
		public void Should_Be_Able_To_Subscribe_Before_Each_Rule_Is_Run()
		{
			_container.RunningRule += checkRunningRule;

			_container.Satisfies(_rule, _rule2);

			_container.AreAllRulesSatisfied();

			Assert.AreEqual(2, _firedRunningRuleEventCount);
		}

		private void checkRunningRule(object sender, EventArgs e)
		{
			_firedRunningRuleEventCount++;
		}

		[Test]
		public void Should_Be_Able_To_Subscribe_After_Each_Rule_Is_Run()
		{
			_container.RanRule += checkRanRule;

			_container.Satisfies(_rule, _rule2);

			_container.AreAllRulesSatisfied();

			Assert.AreEqual(2, _firedRanRuleEventCount);
		}

		private void checkRanRule(object sender, EventArgs e)
		{
			_firedRanRuleEventCount++;
		}

		[Test]
		public void Should_Return_Broken_If_Any_Rule_Is_Broken()
		{
			_container.Satisfies(_rule, _rule2);

			Assert.IsFalse(_container.AreAllRulesSatisfied());
		}

		[Test]
		public void Should_Correctly_Add_Broken_Rules()
		{
			_container.Satisfies(_rule, _rule2);

			_container.AreAllRulesSatisfied();

			Assert.AreEqual(1, _container.BrokenRules.Length);
			Assert.AreEqual(_rule, _container.BrokenRules[0].Rule);
		}

		[Test]
		[ExpectedException(typeof(InvalidOperationException))]
		public void Should_Die_If_Business_Rules_Are_Not_For_Same_Value_Type()
		{
			_container.Satisfies(_rule, _rule3);
		}
	}
}
